#include <led_strip.h>
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "sys/socket.h"
#include "lwip/err.h"
#include "lwip/sys.h"
#include "netdb.h"
#include "arpa/inet.h"
#include <string.h>
#include "esp_log.h"
#include "esp_mn_models.h"
#include "model_path.h"
#include "esp_mn_iface.h"
#include "esp_wn_models.h"
#include "esp_afe_sr_models.h"
#include "esp_afe_sr_iface.h"
#include "driver/i2s.h"
#include "esp_mn_speech_commands.h"
#include "math.h"

#define DATA_PIN 3
#define NUM_LEDS 40
#define BRIGHTNESS 20

led_strip_handle_t led_strip;
void init_led_strip(led_strip_handle_t *led_strip_p)
{
    led_strip_config_t strip_config = {
        .strip_gpio_num = DATA_PIN,
        .max_leds = NUM_LEDS,
        .led_model = LED_MODEL_WS2812,
        .color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB,
        .flags = {
            .invert_out = false,
        }};
    led_strip_rmt_config_t rmt_config = {
        .clk_src = RMT_CLK_SRC_DEFAULT,
        .resolution_hz = 10 * 1000 * 1000,
        .mem_block_symbols = 64,
        .flags = {
            .with_dma = false,
        }};

    ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, led_strip_p));
}

uint32_t grb_arr[] = {
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
    0xffffff,
};

void set_color()
{
    for (int i = 0; i < NUM_LEDS; i++)
    {
        uint32_t color = grb_arr[i];
        uint8_t g = (color >> 16) & 0xff * BRIGHTNESS / 100;
        uint8_t r = (color >> 8) & 0xff * BRIGHTNESS / 100;
        uint8_t b = color & 0xff * BRIGHTNESS / 100;
        ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i, g, r, b));
    }
    ESP_ERROR_CHECK(led_strip_refresh(led_strip));
}


#define WIFI_SSID "Rex-laptop"
#define WIFI_PASS "Wyt157!369"

void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{ 
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
    {
        esp_wifi_connect();
    }
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        esp_wifi_connect();
    }
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
    {
        ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
        printf("Got IP: " IPSTR "\n", IP2STR(&event->ip_info.ip));
    }
}

void initialize_wifi()
{
    esp_netif_init();
    esp_event_loop_create_default();
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));  // 注册WiFi事件处理程序
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL)); // 注册IP事件处理程序

    wifi_config_t wifi_config = {
        .sta = {
            .ssid = WIFI_SSID,
            .password = WIFI_PASS,
            .channel = 6,
        },
    };

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));                   
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); 
    ESP_ERROR_CHECK(esp_wifi_start());                                
}

// 打印 Wi-Fi 信息
void print_wifi_info()
{
    wifi_config_t wifi_config;
    esp_wifi_get_config(ESP_IF_WIFI_STA, &wifi_config);

    esp_netif_ip_info_t ip_info;
    esp_netif_t *netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
    if (netif && esp_netif_get_ip_info(netif, &ip_info) == ESP_OK)
    {
        printf("Wi-Fi SSID: %s", (char *)wifi_config.sta.ssid);
        printf("Wi-Fi Password: %s", (char *)wifi_config.sta.password);
        printf("IP Address: " IPSTR, IP2STR(&ip_info.ip));
    }
    else
    {
        printf("Failed to get IP information");
    }
}

void wifi_init_sta(void)
{
    initialize_wifi(); 
    print_wifi_info(); 
}

#define I2S_DIN 11
#define I2S_BCLK 12
#define I2S_LRC 13
#define SAMPLE_RATE_98357 8000
#define BUFFER_LEN_98357 1024
#define BUFFER_CNT_98357 16

static uint8_t audio_buffer[BUFFER_LEN_98357];  
void Init_i2s_98357()
{
    i2s_config_t i2s_config = {
        .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
        .sample_rate = SAMPLE_RATE_98357,
        .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
        .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, 
        .communication_format = I2S_COMM_FORMAT_STAND_I2S,
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
        .dma_buf_count = BUFFER_CNT_98357,
        .dma_buf_len = BUFFER_LEN_98357 
    };

    i2s_pin_config_t pin_config = {
        .bck_io_num = I2S_BCLK,
        .ws_io_num = I2S_LRC,
        .data_out_num = I2S_DIN,
        .data_in_num = -1};

    i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
    i2s_set_pin(I2S_NUM_0, &pin_config);
    i2s_stop(I2S_NUM_0); 
}

#define BUFFER_LEN 1024
#define BF_CNT 8
#define MIC_WS 40  
#define MIC_SCK 41 
#define MIC_SD 42  
void Init_i2s_INMP441()
{
    i2s_config_t i2s_config = {
        .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
        .sample_rate = 16000,
        .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
        .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
        .communication_format = I2S_COMM_FORMAT_STAND_I2S,
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
        .dma_buf_count = BF_CNT,
        .dma_buf_len = BUFFER_LEN};
    i2s_driver_install(I2S_NUM_1, &i2s_config, 0, NULL);
    i2s_pin_config_t pin_config = {
        .bck_io_num = MIC_SCK,
        .ws_io_num = MIC_WS,
        .data_out_num = -1,
        .data_in_num = MIC_SD};
    i2s_set_pin(I2S_NUM_1, &pin_config);
    i2s_start(I2S_NUM_1);
}

#define TAG "app"
typedef struct
{
    wakenet_state_t wakenet_mode;
    esp_mn_state_t state;
    int command_id;
} sr_result_t;

static QueueHandle_t g_result_que = NULL;
int detect_flag = 0;
static esp_afe_sr_iface_t *afe_handle = NULL;
static model_iface_data_t *model_data = NULL;
static const esp_mn_iface_t *multinet = NULL;

const char *cmd_phoneme[4] = {
    "bo fang yin yue",
    "ting zhi bo fang",
    "ce shi la ba",
    "xia yi qu",
};

typedef enum
{
    BO_FANG_YIN_YUE,
    TING_ZHI_BO_FANG,
    CE_SHI_LA_BA,
    XIA_YI_QU,
} cmd_enum;

void feed_Task(void *arg)
{
    esp_afe_sr_data_t *afe_data = arg;

    int feed_chunksize = afe_handle->get_feed_chunksize(afe_data);
    int feed_nch = afe_handle->get_feed_channel_num(afe_data);
    int16_t *feed_buff = (int16_t *)malloc(feed_chunksize * feed_nch * sizeof(int16_t));

    assert(feed_buff);
    while (1)
    {
        size_t bytesIn = 0;
        i2s_read(I2S_NUM_1, feed_buff, feed_chunksize * feed_nch * sizeof(int16_t), &bytesIn, portMAX_DELAY);
        afe_handle->feed(afe_data, feed_buff);
    }
    if (feed_buff)
    {
        free(feed_buff);
        feed_buff = NULL;
    }
    vTaskDelete(NULL);
}

void detect_Task(void *arg)
{

    esp_afe_sr_data_t *afe_data = arg;
    while (1)
    {
        afe_fetch_result_t *res = afe_handle->fetch(afe_data);
        if (!res || res->ret_value == ESP_FAIL)
        {
            printf("fetch error!\n");
            break;
        }
        if (res->wakeup_state == WAKENET_DETECTED)
        { // 得到唤醒词
            printf("-----------LISTENING-----------\n");
            detect_flag = true;
        }
        else if (res->wakeup_state == WAKENET_CHANNEL_VERIFIED)
        {
            detect_flag = true;
            afe_handle->disable_wakenet(afe_data);
        }

        if (true == detect_flag)
        {
            esp_mn_state_t mn_state = multinet->detect(model_data, res->data);

            if (ESP_MN_STATE_TIMEOUT == mn_state)
            { // 超时
                int8_t result = -1;
                xQueueSend(g_result_que, &result, 10);

                detect_flag = false;
                continue;
            }

            if (ESP_MN_STATE_DETECTED == mn_state)
            { // 得到指令
                esp_mn_results_t *mn_result = multinet->get_results(model_data);

                int sr_command_id = mn_result->command_id[0];

                int8_t result = sr_command_id;

                xQueueSend(g_result_que, &result, 10);
                detect_flag = false;
            }
        }
    }
    vTaskDelete(NULL);
}

#include "esp_http_client.h"

static TaskHandle_t audio_task_handle = NULL;
static bool is_playing = false;
static uint8_t id = 0;

#define AUDIO_STREAM_URL "http://192.168.137.1:8000/audio/"
void audio_play_task(void *pvParameters)
{
    char url[64];
    snprintf(url, sizeof(url), "%s%d", AUDIO_STREAM_URL, id);

    esp_http_client_config_t cfg = {
        .url = url,
        .buffer_size = BUFFER_LEN_98357,
        .method = HTTP_METHOD_GET,
        .disable_auto_redirect = false,
        .timeout_ms = 30000,
    };

    esp_http_client_handle_t httpHandle = esp_http_client_init(&cfg);
    if (!httpHandle)
    {
        ESP_LOGE("AUDIO_PLAY", "Failed to initialize HTTP client");
        vTaskDelete(NULL);
    }

    i2s_start(I2S_NUM_0);

    esp_err_t err = esp_http_client_open(httpHandle, 0);
    if (err != ESP_OK)
    {
        ESP_LOGE("AUDIO_PLAY", "Failed to open connection: 0x%x", err);
        esp_http_client_cleanup(httpHandle);
        i2s_stop(I2S_NUM_0);
        is_playing = false;
        audio_task_handle = NULL;
        vTaskDelete(NULL);
        return;
    }

    int content_length = esp_http_client_fetch_headers(httpHandle);
    ESP_LOGI("AUDIO_PLAY", "Content length: %d", content_length);

    while (is_playing)
    {
        int read_len = esp_http_client_read(httpHandle, (char *)audio_buffer, BUFFER_LEN_98357);
        if (read_len < 0)
        {
            ESP_LOGE("AUDIO_PLAY", "Error reading audio data");
            break;
        }

        if (read_len == 0)
        { // 数据结束
            ESP_LOGI("AUDIO_PLAY", "End of audio stream");
            break;
        }

        size_t bytes_written;

        i2s_write(I2S_NUM_0, audio_buffer, read_len, &bytes_written, portMAX_DELAY);
        
        memcpy(grb_arr, audio_buffer, NUM_LEDS * sizeof(u32_t));
        set_color();

        if ((int)bytes_written < read_len)
        {
            ESP_LOGW("AUDIO_PLAY", "I2S write underflow: %d/%d", bytes_written, read_len);
        }
    }

    esp_http_client_close(httpHandle);
    esp_http_client_cleanup(httpHandle);
    i2s_stop(I2S_NUM_0);
    is_playing = false;
    audio_task_handle = NULL;
    vTaskDelete(NULL);
}

void bo_fang_yin_yue()
{
    if (!is_playing)
    {
        is_playing = true;
        xTaskCreate(audio_play_task, "audio_task", 4096, NULL, 9, &audio_task_handle);
    }
}

void ce_shi_task(void *pvParameters)
{
    i2s_start(I2S_NUM_0);

    const float freq = 1000.0;                
    const int sample_rate = SAMPLE_RATE_98357;
    const int buffer_size = 512;     
    int16_t samples[buffer_size];

    while (is_playing)
    {
        // 生成一批样本
        static float phase = 0;
        for (int i = 0; i < buffer_size; i++)
        {
            samples[i] = (int16_t)(32767 * sin(phase)); 
            phase += 2 * M_PI * freq / sample_rate;
            if (phase >= 2 * M_PI)
                phase -= 2 * M_PI;
        }

        // 发送立体声数据（左右相同）
        size_t bytes_written;
        i2s_write(I2S_NUM_0, samples, buffer_size * sizeof(int16_t), &bytes_written, portMAX_DELAY);
    }

    i2s_stop(I2S_NUM_0);
    is_playing = false;
    vTaskDelete(NULL);
}

void ce_shi_la_ba()
{
    if (!is_playing)
    {
        is_playing = true;
        xTaskCreate(ce_shi_task, "ce_shi_task", 4096, NULL, 5, &audio_task_handle);
    }
}

void ting_zhi_bo_fang()
{
    is_playing = false;
}

void xia_yi_qu()
{
    ting_zhi_bo_fang();
    // 下一曲
    id++;
    // 播放
    bo_fang_yin_yue();
}

void sr_handler_task(void *pvParam)
{
    QueueHandle_t xQueue = (QueueHandle_t)pvParam;
    while (true)
    {
        int8_t result;
        xQueueReceive(xQueue, &result, portMAX_DELAY);
        if (result == -1)
        {
            printf("超时\n");
        }
        else if (result == BO_FANG_YIN_YUE)
        {
            printf("播放音乐\n");
            bo_fang_yin_yue();
        }
        else if (result == TING_ZHI_BO_FANG)
        {
            printf("停止播放\n");
            ting_zhi_bo_fang();
        }
        else if (result == CE_SHI_LA_BA)
        {
            printf("测试喇叭\n");
            ce_shi_la_ba();
        }
        else if (result == XIA_YI_QU)
        {
            printf("下一曲\n");
            xia_yi_qu();
        }
        else
        {
            printf("命令编号 %d\n", result);
        }
    }
}

int app_main()
{
    /* 初始化NVS */
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ESP_ERROR_CHECK(nvs_flash_init());
    }

    init_led_strip(&led_strip);

    set_color();

    Init_i2s_98357();
    Init_i2s_INMP441();
    wifi_init_sta();

    srmodel_list_t *models = esp_srmodel_init("model");
    afe_config_t *afe_config = afe_config_init("M", models, AFE_TYPE_SR, AFE_MODE_LOW_COST);

    afe_config->wakenet_model_name = esp_srmodel_filter(models, ESP_WN_PREFIX, NULL);
    afe_handle = esp_afe_handle_from_config(afe_config);
    esp_afe_sr_data_t *afe_data = afe_handle->create_from_config(afe_config);

    char *mn_name = esp_srmodel_filter(models, ESP_MN_CHINESE, NULL);
    if (NULL == mn_name)
    {
        printf("No multinet model found");
        return ESP_FAIL;
    }
    multinet = esp_mn_handle_from_name(mn_name);
    model_data = multinet->create(mn_name, 5000); // 设置唤醒超时时间
    printf("load multinet:%s", mn_name);
    esp_mn_commands_clear(); // 清除唤醒指令
    for (int i = 0; i < sizeof(cmd_phoneme) / sizeof(cmd_phoneme[0]); i++)
    {
        esp_mn_commands_add(i, (char *)cmd_phoneme[i]); // 逐个将唤醒指令放入
    }
    esp_mn_commands_update(); // 更新命令词列表
    esp_mn_commands_print();
    multinet->print_active_speech_commands(model_data); // 输出目前激活的命令词

    afe_config_free(afe_config);

    g_result_que = xQueueCreate(1, sizeof(sr_result_t));

    xTaskCreatePinnedToCore(&feed_Task, "feed", 8 * 1024, (void *)afe_data, 5, NULL, 0);
    xTaskCreatePinnedToCore(&detect_Task, "detect", 4 * 1024, (void *)afe_data, 5, NULL, 1);
    xTaskCreatePinnedToCore(&sr_handler_task, "SR Handler Task", 4 * 1024, g_result_que, 1, NULL, 1);

    return 0;
}
